/**
interpret
29.11.2011
RA
zatim je to hodne picate, snaha o to, aby to nejak fungovalo, postupne se budou ladit detaily
rev2 snad je to lepsi
**/


/* system */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>

/* local*/
#include "interp.h"
#include "ilist.h"
#include "hash.h"


static void INTERPRET(tIList *L)
{
  initLCACHE(&LABLIST);
  initCACHE(&FCLIST);
  initVCACHE(&VARLIST);

  struct fcTable *labelTable;
  struct intTable *mainTable;
  char *label = "";

  GetNextInst(L, "main");
  labelTable = makeLLIST("main", &LABLIST, L);
  mainTable = makeFLIST("main", &FCLIST);

  while(1)
  {
    GetNextInst(L, label);
    switch (L->Act->Instr)
    {
      case(IS_NOP):
        return 0;
        break;

      case(IS_READ):
        read( &VARLIST, Addr3, Addr1, Addr2);
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;

      case(IS_NOP):
        return 0;
        break;
    }
  }
}




static void EXsearch(inIList *L, char *name)
{
  while ( L->Act->varName != name)
  {
    if ( L->Act->varNext == NULL)
      L->Act = L->First;
    else
      L->Act = L->Act->varNext;
  }
}


static interCache makeFLIST(char *fname, inFList *L)
{
  struct intTable *T;
  if(( T = malloc( sizeof ( struct intTable ))) == NULL)
    assert(0);
  if(L->First == NULL)
    L->First = T;
  else
    L->Act->fNext = T;
  L->Act = T;
  T->FName = fname;
  T->fNext = NULL;
  T->varCache = NULL;
  return T;
}

static fcLabels makeLLIST(char *name, inLabels *L, tIList *P)
{
  struct fcTable *T;
  if (( T = malloc( sizeof ( struct fcTable ))) == NULL)
    assert(0);
  if ( L->First == NULL )
    L->First = T;
  else
    L->Act->fcNext = T;
  L->Act = T;
  T->name = name;
  T->fcNext = NULL;
  T->label = P->Act;
}


static void initCACHE(inFList *L)
{
  L->First = NULL;
  L->Act = NULL;
}

static void initVCACHE(inIList *L)
{
  L->First = NULL;
  L->Act = NULL;
}

static void initLCACHE(inLabels *L)
{
  L->First = NULL;
  L->Act = NULL;
}


static void IN_genvar(inFList *F, inIList *V, char *vname)
{
  struct varTable *T;
  if (( T = malloc( sizeof ( struct varTable ))) == NULL )
    assert(0);
  T->varName = vname;
  T->varNext = NULL;
  T->charVal = NULL;
  T->intVal = DNULL;
  T->boolVal = NULL;

  if (V->First == NULL)
  {
    V->First = T;
    V->Act = T;
    F->Act->varCache = T;
  }
  else
  {
    while (V->Act->varNext != NULL)
    {
      V->Act = V->Act->varNext;
    }
    V->Act->varNext = T;
    V->Act = T;
  }
}


static void IN_movC(inIList *L, char *name, char *val)
{
  EXsearch(L, name);
  L->Act->charVal = val;
  L->Act->intVal = DNULL;
  L->Act->boolVal = NULL;
}



static void IN_movD(inIList *L, char *name, double val)
{
  EXsearch(L, name);
  L->Act->charVal = NULL;
  L->Act->intVal = val;
  L->Act->boolVal = NULL;
}

static void IN_movB(inIList *L, char *name, bool val)
{
  EXsearch(L, name);
  L->Act->charVal = NULL;
  L->Act->intVal = DNULL;
  L->Act->boolVal = val;
}

static void write(inIList *L, char *name)
{
  EXsearch(L, name);
  if ( L->Act->intVal == DNULL)
    printf("%s", L->Act->charVal);
  else
    printf("%g", L->Act->intVal);
}



static void read(inIList *L, char *name, irType rtype, int len)
{
  EXsearch(L, name);
  L->Act->charVal = "";
  L->Act->intVal = DNULL;
  char z;
  switch(rtype)
  {
    case DOUBLE:
      scanf("%f", &L->Act->intVal);
      if ( L->Act->intVal == DNULL )
        return -1;
      L->Act->charVal = NULL;
      break;

    case STRING_EOF:
      while (( z = getchar()) != EOF)
        IN_addchar(L, (L->Act->charVal), &z);
      break;

    case STRING_ENDOFLINE:
      while (( z = getchar()) != '\n')
        IN_addchar(L, (L->Act->charVal), &z);
      break;

    case IN_len:
      while ( len != 0 )
      {
         z = getchar();
         IN_addchar(L, (L->Act->charVal), &z);
         len--;
      }
      break;

    default:
      break;
  }
}



static void IN_add(inIList *L, char *OUT, char *IN_NUM1, char *IN_NUM2)
{
  double t1, t2;
  EXsearch(L, IN_NUM1);
  if ( L->Act->intVal == DNULL )
    return -1;
  t1 = L->Act->intVal;

  EXsearch(L, IN_NUM2);
  if ( L->Act->intVal == DNULL )
    return -1;
  t2 = L->Act->intVal;

  IN_movD(L, OUT, ( t1 + t2 ));
}


static void IN_sub(inIList *L, char *OUT, char *IN_NUM1, char *IN_NUM2)
{
  double t1, t2;
  EXsearch(L, IN_NUM1);
  if ( L->Act->intVal == DNULL )
    return -1;
  t1 = L->Act->intVal;

  EXsearch(L, IN_NUM2);
  if ( L->Act->intVal == DNULL )
    return -1;
  t2 = L->Act->intVal;

  if ( (t1 - t2) < 0)
    return -1;

  IN_movD(L, OUT, ( t1 - t2 ));
}


static void IN_mul(inIList *L, char *OUT, char *IN_NUM1, char *IN_NUM2)
{
  double t1, t2;
  EXsearch(L, IN_NUM1);
  if ( L->Act->intVal == DNULL )
    return -1;
  t1 = L->Act->intVal;


  EXsearch(L, IN_NUM2);
  if ( L->Act->intVal == DNULL )
    return -1;
  t2 = L->Act->intVal;

  IN_movD(L, OUT, ( t1 * t2 ));
}


static void IN_div(inIList *L, char *OUT, char *IN_NUM1, char *IN_NUM2)
{
  double t1, t2;
  EXsearch(L, IN_NUM1);
  if ( L->Act->intVal == DNULL )
    return -1;
  t1 = L->Act->intVal;


  EXsearch(L, IN_NUM2);
  if ( L->Act->intVal == DNULL )
    return -1;
  t2 = L->Act->intVal;
  if ( t2 == 0)
    return -1;

  IN_movD(L, OUT, ( t1 / t2 ));
}


static void IN_neg(inIList *L, char *EO, char *EI)
{
  EXsearch(L, EI);
  if ( L->Act->intVal == DNULL )
    return -1;
  if( L->Act->intVal != 0)
    IN_movD(L, EO, 0);
  else
    IN_movD(L, EO, 1);
}


static void IN_less(inIList *L, char *out, char *in0, char *in1)
{
  EXsearch(L, in0);
  if ( L->Act->intVal != DNULL)
  {
    double t0, t1;

    t0 = L->Act->intVal;

    EXsearch(L, in1);
    if ( L->Act->intVal == DNULL )
      return -1;
    t1 = L->Act->intVal;

    IN_movB(L, out, ((t0 < t1) ? 1 : 0));
  }
  else if ( L->Act->charVal != NULL)
  {
    char *t0, *t1;

    t0 = L->Act->charVal;

    EXsearch(L, in1);
    if ( L->Act->charVal == NULL )
      return -1;
    t1 = L->Act->charVal;

    IN_movB(L, out, (strcmp (t0, t1) < 0 ? 1 : 0));
  }
  else
    return -1;
}

static void IN_lesseq(inIList *L, char *out, char *in0, char *in1)
{
  EXsearch(L, in0);
  if ( L->Act->intVal != DNULL)
  {
    double t0, t1;

    t0 = L->Act->intVal;

    EXsearch(L, in1);
    if ( L->Act->intVal == DNULL )
      return -1;
    t1 = L->Act->intVal;

    IN_movB(L, out, ((t0 <= t1) ? 1 : 0));
  }
  else if ( L->Act->charVal != NULL)
  {
    char *t0, *t1;

    t0 = L->Act->charVal;

    EXsearch(L, in1);
    if ( L->Act->charVal == NULL )
      return -1;
    t1 = L->Act->charVal;

    IN_movB(L, out, (strcmp (t0, t1) <= 0 ? 1 : 0));
  }
  else
    return -1;
}

static void IN_grt(inIList *L, char *out, char *in0, char *in1)
{
  EXsearch(L, in0);
  if ( L->Act->intVal != DNULL)
  {
    double t0, t1;

    t0 = L->Act->intVal;

    EXsearch(L, in1);
    if ( L->Act->intVal == DNULL )
      return -1;
    t1 = L->Act->intVal;

    IN_movB(L, out, ((t0 > t1) ? 1 : 0));
  }
  else if ( L->Act->charVal != NULL)
  {
    char *t0, *t1;

    t0 = L->Act->charVal;

    EXsearch(L, in1);
    if ( L->Act->charVal == NULL )
      return -1;
    t1 = L->Act->charVal;

    IN_movB(L, out, (strcmp (t0, t1) > 0 ? 1 : 0));
  }
  else
    return -1;
}

static void IN_grteq(inIList *L, char *out, char *in0, char *in1)
{
  EXsearch(L, in0);
  if ( L->Act->intVal != DNULL)
  {
    double t0, t1;

    t0 = L->Act->intVal;

    EXsearch(L, in1);
    if ( L->Act->intVal == DNULL )
      return -1;
    t1 = L->Act->intVal;

    IN_movB(L, out, ((t0 >= t1) ? 1 : 0));
  }
  else if ( L->Act->charVal != NULL)
  {
    char *t0, *t1;

    t0 = L->Act->charVal;

    EXsearch(L, in1);
    if ( L->Act->charVal == NULL )
      return -1;
    t1 = L->Act->charVal;

    IN_movB(L, out, (strcmp (t0, t1) >= 0 ? 1 : 0));
  }
  else
    return -1;
}

static void IN_conc(inIList *L, char *s0, char *s1)
{
  char *t0;
  char *t1;
  EXsearch(L, s0);
  if ( L->Act->charVal == NULL )
    return -1;
  t0 = malloc(strlen(L->Act->charVal));
  strcpy(t0, L->Act->charVal);

  EXsearch(L, s1);
  if ( L->Act->charVal == NULL )
    return -1;
  t1 = malloc(strlen(L->Act->charVal));
  strcpy(t1, L->Act->charVal);
  EXsearch(L, s0);
  IN_addchar(L, t0, t1);
  free(t0);
  free(t1);
}

static void IN_addchar(inIList *L, char *s0, char *s1)
{
  int t0, t1;
  t0 = strlen(s0);
  t1 = strlen(s1);
  if ((L->Act->charVal = malloc (t0+t1+1)) == NULL)
    assert(0);
  strcpy (L->Act->charVal, s0);
  strcat ( L->Act->charVal, s1 );
}


static void IN_eq(inIList *L, char *out, char *in0, char *in1)
{
  EXsearch(L, in0);
  if ( L->Act->intVal != DNULL)
  {
    double t0, t1;

    t0 = L->Act->intVal;

    EXsearch(L, in1);
    if ( L->Act->intVal == DNULL )
      IN_movB(L, out, false);
    t1 = L->Act->intVal;

    IN_movB(L, out, ((t0 == t1) ? 1 : 0));
  }
  else if ( L->Act->charVal != NULL)
  {
    char *t0, *t1;

    t0 = L->Act->charVal;

    EXsearch(L, in1);
    if ( L->Act->charVal == NULL )
      IN_movB(L, out, false);
    t1 = L->Act->charVal;

    IN_movB(L, out, (strcmp (t0, t1) == 0 ? 1 : 0));
  }
  else
    return -1;
}

static void IN_neq(inIList *L, char *out, char *in0, char *in1)
{
  EXsearch(L, in0);
  if ( L->Act->intVal != DNULL)
  {
    double t0, t1;

    t0 = L->Act->intVal;

    EXsearch(L, in1);
    if ( L->Act->intVal == DNULL )
      IN_movB(L, out, false);
    t1 = L->Act->intVal;

    IN_movB(L, out, ((t0 != t1) ? 1 : 0));
  }
  else if ( L->Act->charVal != NULL)
  {
    char *t0, *t1;

    t0 = L->Act->charVal;

    EXsearch(L, in1);
    if ( L->Act->charVal == NULL )
      IN_movB(L, out, false);
    t1 = L->Act->charVal;

    IN_movB(L, out, (strcmp (t0, t1) != 0 ? 1 : 0));
  }
  else
    return -1;
}

static char *IN_type(inIList *L, char *name)
{
  char *temp;
  switch ( IN_vartype( L, name) )
  {
    case vard:
      return ( temp = "number" );
      break;

    case varc:
      return (temp = "string");
      break;

    case varb:
      return (temp = "boolean");
      break;

    default:
      break;
  }
}

static char *IN_substr(inIList *L, char *name, int start, int final)
{
  if ( start == 0 )
    return -1;

  EXsearch(L, name);
  if( start < 0 )
    start = (strlen(L->Act->charVal)) + start;
  else
    start = start - 1;
  if ( final++ < 0 )
    final = ( strlen (L->Act->charVal)) + final;
  final = final - start;
  if ( final < 0 )
      final = 0;

  char *temp;
  if (( temp = malloc( final )) == NULL)
      assert(0);
  strncpy(temp, L->Act->charVal + start, final);
  temp[ final ] = '\0';
  return temp;
}


static void IN_POWA(inIList *L, char *name, int powa)
{
  EXsearch(L, name);
  L->Act->intVal = pow ( L->Act->intVal, powa);
}


static void IN_disposeIL(inIList *L)
{
  L->Act = NULL;
  struct varTable *temp;
  while(L->First!=NULL)
  {
    temp = L->First;
    L->First=L->First->varNext;
    free(temp);
  }
}


static void IN_ret(inFList *F, inIList *L, char *name)
{
  double td;
  char *tc;
  bool tb;
  char *n;
  n = F->Act->FName;
  //if ( strcmp ( n, "main") == 0 )
    //return 0;


  switch ( IN_vartype( L, name) )
  {
    case vard:
      td = L->Act->intVal;
      IN_disfnc( F, L, n);
      IN_genvar( F, L, n);
      IN_movD( L, n, td);
      break;

    case varc:
      tc = L->Act->charVal;
      IN_disfnc( F, L, n);
      IN_genvar( F, L, n);
      IN_movC( L, n, tc);
      break;

    case varb:
      tb = L->Act->boolVal;
      IN_disfnc( F, L, n);
      IN_genvar( F, L, n);
      IN_movB( L, n, tb);
      break;

    default:
      break;
  }


}


static irType IN_vartype(inIList *L, char *name)
{
  EXsearch(L, name);
  if ( L->Act->intVal == DNULL)
  {
    if ( L->Act->charVal == NULL)
    {
      if ( L->Act->boolVal == NULL)
        return varb;
      else
        return varn;
    }
    else
      return varc;
  }
  else
    return vard;
}


static void IN_disfnc(inFList *F, inIList *L, char *n)
{
  IN_disposeIL(L);
  if ( F->Act->fNext == NULL)
      F->Act = F->First;

  while ( F->Act->fNext->FName != n)
  {
    if ( F->Act->fNext == NULL)
      F->Act = F->First;
    else
      F->Act = F->Act->fNext;
  }
  initVCACHE(L);
  L->First = F->Act->varCache;
  L->Act = L->First;
  free(F->Act->fNext);
  F->Act->fNext = NULL;
}


static char *IN_goto(inIList *L, char *name, char *label)
{
  char *temp = "";
  EXsearch( L, name);
  return (( L->Act->boolVal == true) ? label : temp);
}
